import { Controller, Post, Body, Patch, UploadedFile, UseInterceptors, Get, Param, Res } from "@nestjs/common";
import { ApiBearerAuth, ApiBody, ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger";
import { UserService } from "./user.service";
import { CreateUserDto } from "./dto/create-user.dto";
import { UpdateUserDto } from "./dto/update-user.dto";
import { Public } from "src/auth/auth.decorator";
import { FileInterceptor } from "@nestjs/platform-express";
import { execSync, ChildProcessWithoutNullStreams, spawn } from "child_process";
import * as path from "path";
import * as fs from "fs";
import { Response } from "express";

@Controller("user")
@ApiTags("用户相关")
export class UserController {
  constructor(private readonly userService: UserService) {
    // 预热长驻ASR服务，避免首个请求的加载时延
    try {
      this.ensureAsrServer(process.cwd());
    } catch {}
  }

  // ========== Whisper 长驻进程（服务器模式）复用，显著降低首次后识别延迟 ==========
  private static asrProcess: ChildProcessWithoutNullStreams | null = null;
  private static asrStdoutBuffer = "";
  private static asrPending = new Map<string, { resolve: (v: string) => void; reject: (e: any) => void; timer: NodeJS.Timeout }>();

  private ensureAsrServer(serverRoot: string) {
    if (UserController.asrProcess) return;
    const pythonScript = path.join(serverRoot, "whisper_rtx4090.py");
    if (!fs.existsSync(pythonScript)) return; // 无脚本则不启用长驻

    // 启动服务器模式
    const child = spawn('python', [pythonScript, '--server'], {
      stdio: ['pipe', 'pipe', 'pipe'],
      env: { ...process.env, PYTHONIOENCODING: 'utf-8', PYTHONUTF8: '1' },
    });
    UserController.asrProcess = child;
    UserController.asrStdoutBuffer = "";

    child.stdout.on('data', (data: Buffer) => {
      UserController.asrStdoutBuffer += data.toString();
      let idx: number;
      while ((idx = UserController.asrStdoutBuffer.indexOf('\n')) >= 0) {
        const line = UserController.asrStdoutBuffer.slice(0, idx).trim();
        UserController.asrStdoutBuffer = UserController.asrStdoutBuffer.slice(idx + 1);
        if (!line) continue;
        try {
          const json = JSON.parse(line);
          const id = json?.id as string | undefined;
          const pending = id ? UserController.asrPending.get(id) : undefined;
          const text = Array.isArray(json?.results)
            ? json.results.map((r: any) => r?.text || "").join("")
            : '';
          if (pending) {
            clearTimeout(pending.timer);
            UserController.asrPending.delete(id!);
            pending.resolve(text || "");
          }
        } catch {
          // 忽略单行解析失败
        }
      }
    });

    child.stderr.on('data', () => {
      // 保持安静，日志由python脚本处理
    });

    const cleanupAll = (reason: string) => {
      UserController.asrPending.forEach((p, id) => {
        clearTimeout(p.timer);
        p.reject(new Error(`ASR server stopped: ${reason}`));
      });
      UserController.asrPending.clear();
      UserController.asrProcess = null;
    };

    child.on('close', (code) => cleanupAll(`exit ${code}`));
    child.on('error', (err) => cleanupAll(err?.message || 'error'));
  }

  private requestAsr(serverRoot: string, audioPath: string, timeoutMs = 120000): Promise<string> {
    this.ensureAsrServer(serverRoot);
    if (!UserController.asrProcess) {
      return Promise.reject(new Error('ASR server is not available'));
    }
    const id = `${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
    const payload = JSON.stringify({ id, path: audioPath }) + "\n";
    return new Promise((resolve, reject) => {
      const timer = setTimeout(() => {
        UserController.asrPending.delete(id);
        reject(new Error('ASR request timed out'));
      }, timeoutMs);
      UserController.asrPending.set(id, { resolve, reject, timer });
      try {
        UserController.asrProcess!.stdin.write(payload, 'utf-8');
      } catch (e) {
        clearTimeout(timer);
        UserController.asrPending.delete(id);
        reject(e);
      }
    });
  }

  private async transcodeToWavIfNeeded(inputPath: string): Promise<string> {
    const ext = path.extname(inputPath).toLowerCase();
    if (ext !== '.webm') return inputPath;
    const outputPath = `${inputPath}.tmp.wav`;
    return await new Promise<string>((resolve, reject) => {
      const trimSilence = process.env.ASR_TRIM_SILENCE === '1';
      const args = ['-y', '-i', inputPath, '-ar', '16000', '-ac', '1'];
      if (trimSilence) {
        // 移除前后静音，降低模型无效处理时间（不改文本准确度）
        args.push('-af', 'silenceremove=1:0:-40dB,areverse,silenceremove=1:0:-40dB,areverse');
      }
      args.push('-f', 'wav', outputPath);
      const ff = spawn('ffmpeg', args, {
        stdio: ['ignore', 'ignore', 'ignore'],
      });
      ff.on('close', (code) => {
        if (code === 0 && fs.existsSync(outputPath)) resolve(outputPath);
        else reject(new Error('音频转换失败，缺少ffmpeg或文件格式不支持'));
      });
      ff.on('error', (err) => reject(err));
    });
  }
  // * 登录
  @Post("/login")
  @Public(true)
  @ApiOperation({ summary: "登录", description: "登录" }) // * 接口描述
  @ApiResponse({ status: 200, description: "用户信息" }) // * 返回信息
  @ApiBody({ type: CreateUserDto }) // * @ApiProperty() 不生效的情况下,可使用 @ApiBody() 显式设置请求体的定义
  login(@Body() createUserDto: CreateUserDto) {
    return this.userService.login(createUserDto);
  }
  // * 注册
  @Post("/register")
  @Public(true)
  @ApiOperation({ summary: "注册", description: "注册" })
  @ApiResponse({ status: 200, description: "success" })
  @ApiBody({ type: CreateUserDto })
  create(@Body() createUserDto: CreateUserDto) {
    return this.userService.register(createUserDto);
  }
  // * 更新
  @Patch("/updata")
  @ApiOperation({ summary: "更新用户信息", description: "更新用户信息" })
  @ApiResponse({ status: 200, description: "success" })
  @ApiBody({ type: UpdateUserDto })
  @ApiBearerAuth()
  updata(@Body() updateUserDto: UpdateUserDto) {
    return this.userService.updata(updateUserDto);
  }

  @Public(true)
  @Post("speech")
  @UseInterceptors(FileInterceptor("file"))
  async speechToText(@UploadedFile() file: Express.Multer.File) {
    const serverRoot = process.cwd();
    const ext = path.extname(file.originalname) || '.webm';
    const tempPath = path.join(serverRoot, `upload_${Date.now()}_${Math.random().toString(36).slice(2,8)}${ext}`);
    fs.writeFileSync(tempPath, file.buffer);
    
    // 优先使用GPU加速的Whisper模型，如果不存在则回退到增强版
    let pythonScript = path.join(serverRoot, "whisper_rtx4090.py");
    if (!fs.existsSync(pythonScript)) {
      pythonScript = path.join(serverRoot, "speech_recognize_paraformer_enhanced.py");
      if (!fs.existsSync(pythonScript)) {
        pythonScript = path.join(serverRoot, "speech_recognize_paraformer.py");
      }
    }
    
    try {
      let text = '';
      let asrInputPath = tempPath;
      try {
        asrInputPath = await this.transcodeToWavIfNeeded(tempPath);
      } catch (convErr) {
        // 转码失败则仍尝试原始格式
      }
      // 优先尝试长驻ASR服务器（仅当whisper_rtx4090.py存在时）
      if (fs.existsSync(path.join(serverRoot, 'whisper_rtx4090.py'))) {
        try {
          text = await this.requestAsr(serverRoot, asrInputPath, 120000);
        } catch {
          // 失败则回退到一次性进程
        }
      }
      if (!text) {
        // 回退到一次性子进程执行
        const pythonProcess = spawn('python', [pythonScript, asrInputPath], {
          stdio: ['pipe', 'pipe', 'pipe'],
          env: { ...process.env, PYTHONIOENCODING: 'utf-8', PYTHONUTF8: '1' },
        });
        let stdout = '';
        let stderr = '';
        pythonProcess.stdout.on('data', (data) => { stdout += data.toString(); });
        pythonProcess.stderr.on('data', (data) => { stderr += data.toString(); });
        await new Promise<void>((resolve, reject) => {
          pythonProcess.on('close', (code) => {
            if (code === 0) resolve();
            else reject(new Error(`Python脚本执行失败，退出码: ${code}\n错误输出: ${stderr}`));
          });
          pythonProcess.on('error', (error) => reject(new Error(`启动Python进程失败: ${error.message}`)));
        });
        const jsonArr = JSON.parse(stdout);
        text = jsonArr.map((r: any) => r.text || "").join("");
      }
      try {
        if (fs.existsSync(tempPath)) {
          fs.unlinkSync(tempPath);
        }
        const tmpWav = `${tempPath}.tmp.wav`;
        if (fs.existsSync(tmpWav)) {
          fs.unlinkSync(tmpWav);
        }
      } catch (e) {
        console.warn('删除临时音频文件失败:', e.message);
      }
      return { text };
    } catch (e) {
      // 删除临时文件（加容错）
      try {
        if (fs.existsSync(tempPath)) {
          fs.unlinkSync(tempPath);
        }
        const tmpWav = `${tempPath}.tmp.wav`;
        if (fs.existsSync(tmpWav)) {
          fs.unlinkSync(tmpWav);
        }
      } catch (err) {
        console.warn('删除临时音频文件失败:', err.message);
      }
      console.error('Python执行异常:', e.message);
      return { error: e.message };
    }
  }

  @Public(true)
  @Post("tts")
  @ApiOperation({ summary: "文字转语音", description: "将文字转换为语音并下载" })
  @ApiResponse({ status: 200, description: "音频文件下载链接" })
  @ApiBody({ 
    schema: {
      type: 'object',
      properties: {
        text: { type: 'string', description: '要转换的文字内容（单文本模式）' },
        conversation: { 
          type: 'array', 
          description: '对话数据（多声音模式）',
          items: {
            type: 'object',
            properties: {
              role: { type: 'string', enum: ['user', 'assistant'] },
              content: { type: 'string' },
              reasoningContent: { type: 'string' }
            }
          }
        }
      }
    }
  })
  async textToSpeech(@Body() body: { text?: string; conversation?: any[] }) {
    const serverRoot = process.cwd();
    const pythonScript = path.join(serverRoot, "text_to_speech_edge_offline.py");
    const outputDir = path.join(serverRoot, "uploads");
    const timestamp = Date.now();
    // 改为mp3以缩短总耗时（避免最终转码）
    const outputPath = path.join(outputDir, `tts_${timestamp}.mp3`);
    
    console.log('=== TTS请求开始 ===');
    console.log('请求参数:', {
      hasText: !!body.text,
      hasConversation: !!body.conversation,
      conversationLength: body.conversation?.length || 0
    });
    console.log('输出路径:', outputPath);
    
    try {
      // 确保输出目录存在
      if (!fs.existsSync(outputDir)) {
        fs.mkdirSync(outputDir, { recursive: true });
        console.log('创建输出目录:', outputDir);
      }
      
      let inputData: string;
      
      if (body.conversation && Array.isArray(body.conversation) && body.conversation.length > 0) {
        // 多声音模式：传递对话数据
        console.log('使用多声音模式');
        
        // 过滤掉欢迎消息
        const filteredConversation = body.conversation.filter(msg => 
          msg.role !== 'assistant' || msg.content !== 'Hi~ 我是`鼎盛` 您身边的智能助手，可以为你答疑解惑、精读文档、尽情创作 让鼎盛助您轻松工作，多点生活。'
        );
        
        console.log('过滤后对话条数:', filteredConversation.length);
        inputData = JSON.stringify(filteredConversation);
        console.log('JSON数据长度:', inputData.length);
      } else if (body.text) {
        // 单文本模式：传递单个文本
        console.log('使用单文本模式');
        const cleanText = body.text.replace(/\n/g, ' ').replace(/\r/g, ' ').trim();
        console.log('文本长度:', cleanText.length);
        inputData = cleanText;
      } else {
        console.log('错误: 没有提供有效的输入参数');
        return { error: "请提供text或conversation参数" };
      }
      
      // 调用Python脚本进行TTS转换
      console.log('开始调用Python脚本...');
      
      // 创建临时JSON文件来传递数据，避免命令行参数长度限制
      const tempJsonFile = path.join(outputDir, `tts_input_${timestamp}.json`);
      fs.writeFileSync(tempJsonFile, inputData, 'utf-8');
      
      console.log('创建临时JSON文件:', tempJsonFile);
      console.log('JSON数据长度:', inputData.length);
      
      // 优先使用专用虚拟环境python或环境变量指定的python，最后回退系统python
      const venvPython = path.join(serverRoot, ".venv_xtts", process.platform === 'win32' ? "Scripts\\python.exe" : "bin/python");
      const pythonExe = process.env.TTS_PYTHON && fs.existsSync(process.env.TTS_PYTHON)
        ? process.env.TTS_PYTHON
        : (fs.existsSync(venvPython) ? venvPython : 'python');

      const result = execSync(`"${pythonExe}" "${pythonScript}" "${tempJsonFile}" "${outputPath}"`, { 
        encoding: "utf-8",
        maxBuffer: 1024 * 1024 * 10, // 10MB buffer
        timeout: 600000, // 10分钟超时，避免长文本处理被中断
        env: {
          ...process.env,
          PYTHONIOENCODING: 'utf-8',
          PYTHONUTF8: '1'
        }
      });
      
      // 清理临时JSON文件
      try {
        fs.unlinkSync(tempJsonFile);
        console.log('清理临时JSON文件');
      } catch (e) {
        console.log('清理临时JSON文件失败:', e);
      }
      
      console.log('Python脚本执行完成');
      console.log('Python输出长度:', result.length);
      console.log('Python输出前100字符:', result.substring(0, 100));
      
      try {
        const ttsResult = JSON.parse(result);
        console.log('JSON解析成功:', ttsResult);
        
        if (ttsResult.success && fs.existsSync(outputPath)) {
          // 返回音频文件下载链接
          const fileName = path.basename(outputPath);
          const fileSize = fs.statSync(outputPath).size;
          console.log('音频文件生成成功:', {
            fileName: fileName,
            fileSize: fileSize,
            filePath: outputPath
          });
          
          return {
            success: true,
            audioUrl: `/dev-api/user/download/tts/${fileName}`,
            fileName: fileName,
          };
        } else {
          console.log('TTS转换失败:', ttsResult);
          return { error: ttsResult.error || "TTS转换失败" };
        }
      } catch (parseError) {
        console.error('TTS结果解析失败:', parseError);
        console.error('原始输出:', result);
        return { error: "TTS结果解析失败" };
      }
    } catch (e) {
      console.error('=== TTS执行异常 ===');
      console.error('异常类型:', e.constructor.name);
      console.error('异常消息:', e.message);
      if (e.stdout) console.error('stdout长度:', e.stdout.toString().length);
      if (e.stderr) console.error('stderr长度:', e.stderr.toString().length);
      if (e.stdout) console.error('stdout前200字符:', e.stdout.toString().substring(0, 200));
      if (e.stderr) console.error('stderr前200字符:', e.stderr.toString().substring(0, 200));
      
      // 不返回详细的错误信息，避免暴露大量数据
      return { error: "语音生成失败，请稍后重试" };
    }
  }

  @Public(true)
  @Get("download/tts/:filename")
  @ApiOperation({ summary: "下载TTS音频文件", description: "下载生成的音频文件" })
  async downloadTTSAudio(@Param('filename') filename: string, @Res() res: Response) {
    const serverRoot = process.cwd();
    const filePath = path.join(serverRoot, "uploads", filename);
    
    if (fs.existsSync(filePath)) {
      const fileBuffer = fs.readFileSync(filePath);
      const ext = path.extname(filename).toLowerCase();
      const contentType = ext === '.mp3' ? 'audio/mpeg' : 'audio/wav';
      res.setHeader('Content-Type', contentType);
      res.setHeader('Content-Disposition', `attachment; filename="${filename}"`);
      res.send(fileBuffer);
    } else {
      res.status(404).json({ error: "音频文件不存在" });
    }
  }
}
